package com.tfswx.nginxcleanhc.util;


import com.sun.jna.Platform;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * @author Ricky
 * @date 2020/11/12
 */
@Slf4j
public class CmdUtil {
    private static final String[] LINUX_CMD = {"/bin/sh", "-c", ""};
    private static final String[] WINDOWS_CMD = {"cmd", "/c", ""};

    private static int execCommand(String commandStr, File dir) throws IOException, InterruptedException {
        return execCommand(commandStr, dir, null);
    }

    private static int execCommand(String commandStr, File dir, Consumer<String> terminalCallback) throws IOException, InterruptedException {
        log.info("使用终端执行命令：【cd {} && {}】", dir.getCanonicalPath(), commandStr);
        String[] cmd;
        if (Platform.isLinux()) {
            cmd = LINUX_CMD;
        } else if (Platform.isWindows()) {
            cmd = WINDOWS_CMD;
        } else {
            throw new RuntimeException("暂不支持该平台");
        }
        cmd[2] = commandStr;
        Process process = Runtime.getRuntime().exec(cmd, null, dir);

        CountDownLatch latch = new CountDownLatch(2);
        new StreamGobbler(latch, process.getErrorStream(), terminalCallback, "ERROR");
        new StreamGobbler(latch, process.getInputStream(), terminalCallback, "OUTPUT");
        //        process.wait();
        process.waitFor();
        latch.await();
        int exitVal = process.exitValue();
        process.destroy();

        log.debug("命令退出代码: {}", exitVal);
        return exitVal;
    }

    public static int exec(String commandStr) throws IOException, InterruptedException {
        return exec(commandStr, ".");
    }

    public static int exec(String commandStr, String path) throws IOException, InterruptedException {
        return exec(commandStr, new File(path));
    }

    public static int exec(String commandStr, File dir) throws IOException, InterruptedException {
        return execCommand(commandStr, dir);
    }

    public static int execWithListener(String commandStr, Consumer<String> terminalCallback) throws IOException, InterruptedException {
        return execWithListener(commandStr, ".", terminalCallback);
    }

    public static int execWithListener(String commandStr, String dirPath, Consumer<String> terminalCallback) throws IOException, InterruptedException {
        return execWithListener(commandStr, new File(dirPath), terminalCallback);
    }

    public static int execWithListener(String commandStr, File dir, Consumer<String> terminalCallback) throws IOException, InterruptedException {
        return execCommand(commandStr, dir, terminalCallback);
    }

    public static String execToString(String commandStr) throws IOException, InterruptedException {
        return execToString(commandStr, ".");
    }

    public static String execToString(String commandStr, String dirPath) throws IOException, InterruptedException {
        return execToString(commandStr, new File(dirPath));
    }

    public static String execToString(String commandStr, File dir) throws IOException, InterruptedException {
        AtomicReference<String> string = new AtomicReference<>("");
        Consumer<String> terminalCallback = out -> string.set(string.get() + out + "\n");
        int exitVal = execCommand(commandStr, dir, terminalCallback);
        return string.get();
    }

    public static List<String> execToList(String commandStr) throws IOException, InterruptedException {
        return execToList(commandStr, ".");
    }

    public static List<String> execToList(String commandStr, String dirPath) throws IOException, InterruptedException {
        return execToList(commandStr, new File(dirPath));
    }

    public static List<String> execToList(String commandStr, File dir) throws IOException, InterruptedException {
        List<String> res = new ArrayList<>();
        Consumer<String> terminalCallback = res::add;
        int exitVal = execCommand(commandStr, dir, terminalCallback);
        return res;
    }

    // public interface TerminalCallback {
    //     void output(String out);
    // }

    @Slf4j
    static class StreamGobbler extends Thread {

        private final InputStream is;
        private final String type;
        private final Consumer callback;
        private final CountDownLatch latch;

        public StreamGobbler(@NonNull CountDownLatch latch, @NonNull InputStream is, Consumer<String> callback, @NonNull String type) {
            this.is = is;
            this.type = type;
            this.callback = callback;
            this.latch = latch;

            this.start();
        }

        @Override
        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(is, "GBK");
                BufferedReader br = new BufferedReader(isr);
                String line;
                while ((line = br.readLine()) != null) {
                    if (callback == null) {
                        System.out.println(type + "\t" + ">>>" + "\t" + line);
                    } else {
                        callback.accept(line);
                    }
                }
                br.close();
                isr.close();
            } catch (IOException ioe) {
                log.error("读取终端结果异常", ioe);
            } finally {
                latch.countDown();
            }
        }
    }
}
